package org.marverse.serialization

import java.nio.ByteBuffer
import scala.collection.mutable.Buffer
import scala.collection.mutable.Map
import util.{MutableTypedMap, TypedKey}

trait ObjectInput {
  @throws(classOf[java.io.IOException])
  def readBytes(buffer: ByteBuffer)
  @throws(classOf[java.io.IOException])
  def readBoolean: Boolean
  @throws(classOf[java.io.IOException])
  def readChar: Char
  @throws(classOf[java.io.IOException])
  def readInt: Int
  @throws(classOf[java.io.IOException])
  def readLong: Long
  @throws(classOf[java.io.IOException])
  def readDouble: Double
  @throws(classOf[java.io.IOException])
  def read[T <: AnyRef](clazz: Class[T])(implicit ctx: MutableTypedMap[TypedKey, Any]): T
  
  @throws(classOf[java.io.IOException])
  def readBuffer[T <: AnyRef, B <: Buffer[T]](buffer: B, clazz: Class[T])(implicit ctx: MutableTypedMap[TypedKey, Any]): B = {
    var size = readInt
    while (size > 0) {
      buffer += read(clazz)
      size -= 1
    }
    buffer
  }

  @throws(classOf[java.io.IOException])
  def readMap[K <: AnyRef, V <: AnyRef, M <: Map[K, V]]
             (map: M, keyClazz: Class[K], valueClazz: Class[V])(implicit ctx: MutableTypedMap[TypedKey, Any]): M = {
    var size = readInt
    while (size > 0) {
      val key = read(keyClazz)
      val value = read(valueClazz)
      map += ((key, value))
      size -= 1
    }
    map
  }

  @throws(classOf[java.io.IOException])
  def readMap[K <: AnyRef, V <: AnyRef, M <: Map[K, V]]
             (map: M, keyKey: TypedKey[K], valueClazz: Class[V])(implicit ctx: MutableTypedMap[TypedKey, Any]): M = {
    val saveOriginalValue = ctx.get(keyKey)
    var size = readInt
    while (size > 0) {
      val key = read(keyKey.valueClass)
      ctx(keyKey) = key
      val value = read(valueClazz)
      saveOriginalValue match { case Some(v) => ctx(keyKey) = v; case None => ctx -= keyKey }
      map += ((key, value))
      size -= 1
    }
    map
  }
}

trait Instantiater[T <: AnyRef] {
  @throws(classOf[java.io.IOException])
  def instantiate(in: ObjectInput, id: ClassRegistry#ID, ctx: MutableTypedMap[TypedKey, Any]): T
}

trait Deserializer[T <: AnyRef] {
  @throws(classOf[java.io.IOException])
  def deserialize(that: T, in: ObjectInput, id: ClassRegistry#ID, ctx: MutableTypedMap[TypedKey, Any])
}
